<template>
  <nly-container fluid tag="main" class="pb-5">
    <!-- Introduction -->
    <div class="bd-content mb-4">
      <h1 style="margin-top:">
        <span class="bd-content-title">{{ title }}</span>
      </h1>
      <nly-row row-class="align-items-center">
        <nly-col>
          <p class="bd-lead">
            您可以在这里测试有关NlyAdminlteVue的任何组件指令代码，在使用之前请您先查看
            <nly-link to="/docs">文档</nly-link> 部分，以获知使用方法
          </p>
        </nly-col>
        <nly-col lg="auto"> </nly-col>
      </nly-row>
    </div>

    <!-- Compiler loading state -->
    <nly-row v-if="loading">
      <nly-col class="mb-2 mb-md-0">
        <!-- Loading indicator -->
        <b-alert variant="info" class="text-center" show>
          Loading JavaScript compiler...
        </b-alert>
      </nly-col>
    </nly-row>

    <!-- Transpiler warning -->
    <nly-container v-if="ready && needsTranspiler">
      <nly-row>
        <nly-col>
          <nly-alert variant="info" class="mb-3" show fade dismissible>
            Your browser does not support modern ES6 JavaScript syntax. However,
            the code in the JavaScipt editor will be transpiled to work with
            your browser, except for any ES6 code that is in the Template editor
            (i.e. destructuring, arrow functions, etc.)
          </nly-alert>
        </nly-col>
      </nly-row>
    </nly-container>

    <!-- Actions -->
    <nly-row>
      <nly-col class="mb-2 mb-md-0">
        <!-- Reset action -->
        <nly-button
          size="sm"
          variant="danger"
          :disabled="isDefault || isBusy"
          @click="reset"
        >
          重置为默认代码
        </nly-button>
      </nly-col>

      <!-- Export actions -->
      <nly-col md="auto" class="mt-2 mt-md-0">
        <b class="d-block d-sm-inline-block mr-sm-2 mb-1 mb-sm-0">Export to</b>

        <!-- Export to CodePen -->
        <nly-form
          class="d-inline-block mr-1 notranslate"
          translate="no"
          method="post"
          action="https://codepen.io/pen/define"
          target="_blank"
        >
          <input type="hidden" name="data" :value="codepenData" />
          <nly-button
            size="sm"
            type="submit"
            :disabled="!isOk || isBusy"
            bg-variant="pink"
            >CodePen</nly-button
          >
        </nly-form>

        <!-- Export to CodeSandbox -->
        <nly-form
          class="d-inline-block mr-1 notranslate"
          translate="no"
          method="post"
          action="https://codesandbox.io/api/v1/sandboxes/define"
          target="_blank"
        >
          <input type="hidden" name="parameters" :value="codesandboxData" />
          <nly-button
            size="sm"
            type="submit"
            :disabled="!isOk || isBusy"
            bg-variant="orange"
            >CodeSandbox</nly-button
          >
        </nly-form>

        <!-- Export to JSFiddle -->
        <nly-form
          class="d-inline-block notranslate"
          translate="no"
          method="post"
          action="https://jsfiddle.net/api/post/library/pure/"
          target="_blank"
        >
          <input type="hidden" name="html" :value="exportData.extendedHtml" />
          <input type="hidden" name="js" :value="exportData.extendedJs" />
          <input
            type="hidden"
            name="resources"
            :value="
              [...exportData.externalCss, exportData.externalJs].join(',')
            "
          />
          <input type="hidden" name="css" :value="exportData.css" />
          <input type="hidden" name="js_wrap" value="l" />
          <nly-button
            size="sm"
            type="submit"
            :disabled="!isOk || isBusy"
            bg-variant="green"
            >JSFiddle</nly-button
          >
        </nly-form>
      </nly-col>
    </nly-row>

    <!-- Editors / Result / Console -->
    <transition-group tag="div" class="row" name="flip">
      <!-- Left/Top column -->
      <nly-col key="A" :cols="full ? 12 : null">
        <transition-group tag="div" class="row" name="flip">
          <!-- Template column -->
          <nly-col
            key="A1"
            :md="vertical && !full ? '6' : '12'"
            sm="12"
            class="mt-3"
          >
            <!-- Template -->
            <nly-card tag="header">
              <nly-card-header>
                <div class="d-flex justify-content-between align-items-center">
                  <h5 class="mb-0">
                    <span class="notranslate" translate="no">Template</span>
                  </h5>
                  <nly-button
                    size="sm"
                    variant="outlineInfo"
                    class="d-none d-md-inline-block"
                    @click="toggleFull"
                  >
                    <span>{{ full ? "半屏" : "全屏" }}</span>
                  </nly-button>
                </div>
              </nly-card-header>
              <nly-card-body>
                <NlyDocsCodeMirror
                  v-model="html"
                  mode="htmlmixed"
                ></NlyDocsCodeMirror>
              </nly-card-body>
            </nly-card>
          </nly-col>

          <!-- JavaScript column -->
          <nly-col
            key="A2"
            :md="vertical && !full ? '6' : '12'"
            sm="12"
            class="mt-3"
          >
            <!-- JavaScript -->
            <nly-card tag="header">
              <nly-card-header>
                <div class="d-flex justify-content-between align-items-center">
                  <h5 class="mb-0">
                    <span class="notranslate" translate="no">JavaScript</span>
                    <small v-if="compiling" class="text-muted">
                      compiling</small
                    >
                  </h5>
                  <nly-button
                    size="sm"
                    variant="outlineInfo"
                    class="d-none d-md-inline-block"
                    @click="toggleFull"
                  >
                    <span>{{ full ? "半屏" : "全屏" }}</span>
                  </nly-button>
                </div>
              </nly-card-header>

              <nly-card-body>
                <NlyDocsCodeMirror
                  v-model="js"
                  mode="javascript"
                ></NlyDocsCodeMirror>
              </nly-card-body>
            </nly-card>
          </nly-col>
        </transition-group>
      </nly-col>

      <!-- Right/bottom column -->
      <nly-col key="B" :md="vertical || full ? '12' : '6'" sm="12">
        <nly-row>
          <!-- Result column -->
          <nly-col xs="12" class="mt-3">
            <!-- Result -->
            <nly-card class="play-result" tag="header">
              <nly-card-header>
                <div class="d-flex justify-content-between align-items-center">
                  <h5 class="mb-0">
                    <span>Result</span>
                    <small v-if="compiling || building" class="text-muted">
                      building</small
                    >
                  </h5>
                  <nly-button
                    v-if="!full"
                    size="sm"
                    variant="outlineInfo"
                    class="d-none d-md-inline-block"
                    @click="toggleVertical"
                  >
                    <span>{{ vertical ? "水平" : "垂直" }}</span>
                  </nly-button>
                </div>
              </nly-card-header>

              <nly-card-body
                ref="result"
                class="play-result-body notranslate"
                translate="no"
              ></nly-card-body>
            </nly-card>
          </nly-col>

          <!-- Console column -->
          <nly-col xs="12" class="mt-3 notranslate" translate="no">
            <!-- Console -->
            <nly-card tag="header">
              <nly-card-header>
                <div class="d-flex justify-content-between align-items-center">
                  <h5 class="mb-0">
                    <span>Console log</span>
                  </h5>
                  <nly-button
                    :disabled="messages.length === 0"
                    size="sm"
                    variant="outlineDanger"
                    @click="clear"
                  >
                    <span>清空</span>
                  </nly-button>
                </div>
              </nly-card-header>

              <nly-card-body>
                <transition-group
                  tag="ul"
                  name="flip-list"
                  class="list-group list-group-flush play-log"
                >
                  <nly-list-group-item
                    v-if="!messages.length"
                    key="empty-console"
                  >
                    &nbsp;
                  </nly-list-group-item>
                  <nly-list-group-item
                    v-for="msg in messages"
                    :key="`console-${msg[2]}`"
                    class="py-2 d-flex"
                  >
                    <nly-badge
                      :variant="msg[0]"
                      class="mr-1"
                      style="font-size:90%;"
                    >
                      {{
                        msg[0] === "danger"
                          ? "error"
                          : msg[0] === "warning"
                          ? "warn"
                          : "log"
                      }}
                    </nly-badge>
                    <span
                      :class="[
                        `text-${msg[0]}`,
                        'text-monospace',
                        'small',
                        'd-block'
                      ]"
                      style="white-space: pre-wrap;"
                      >{{ msg[1] }}</span
                    >
                  </nly-list-group-item>
                </transition-group>
              </nly-card-body>
            </nly-card>
          </nly-col>
        </nly-row>
      </nly-col>
    </transition-group>
  </nly-container>
</template>

<style>
.play-result-body,
.play-log {
  min-height: 300px;
}
.flip-move {
  transition: all 0.3s;
}
.play-log .list-group-item {
  transition: all 0.3s;
}
.flip-list-enter,
.flip-list-leave-to {
  opacity: 0;
}
.flip-list-leave-active {
  position: absolute;
}
.flip-list-move {
  transform: 0.3s;
}
.bv-carbon-ad {
  min-height: 130px;
}
@media (min-width: 992px) {
  .bv-carbon-ad {
    min-width: 330px;
  }
}
</style>

<script>
import Vue from "vue";
import debounce from "lodash/debounce";
import { getParameters as getCodeSandboxParameters } from "codesandbox/lib/api/define";
import needsTranspiler from "~/utils/needs-transpiler";
import { version as nlyAdminlteVueVersion, vueVersion } from "~/content";
import NlyDocsCodeMirror from "../components/nly-docs-code-mirror";
// --- Constants ---
const DEFAULT_HTML = `<div>
  <nly-button variant='warning' size="sm" @click="toggle">
    {{ show ? 'Hide' : 'Show' }}
  </nly-button>
  <nly-badge variant='info' v-show='show'>
    Hello {{ name }}!
  </nly-badge>
</div>`;
const DEFAULT_JS = `{
  data() {
    return {
      name: 'NlyAdminlteVue',
      show: true
    }
  },
  watch: {
    show(newVal) {
      console.log('Badge is now ' + (newVal ? 'visible' : 'hidden'))
    }
  },
  methods: {
    toggle() {
      console.log('Toggle button clicked')
      this.show = !this.show
    }
  }
}`;
const storage =
  typeof window !== "undefined" ? window.localStorage || null : null;
const STORAGE_KEY_PREFIX = "VNLY_playground";
const STORAGE_KEYS = {
  html: `${STORAGE_KEY_PREFIX}_html`,
  js: `${STORAGE_KEY_PREFIX}_js`,
  layout: `${STORAGE_KEY_PREFIX}_layout`,
  timestamp: `${STORAGE_KEY_PREFIX}_ts`
};
// Maximum age of localStorage before we revert back to defaults
const STORAGE_MAX_RETENTION = 7 * 24 * 60 * 60 * 1000; // 7 days
// --- Helper functions ---
// Remove a node from its parent's children
const removeNode = node =>
  node && node.parentNode && node.parentNode.removeChild(node);
// Indent a value by the given count
const indent = (value, count = 2, { indent } = { indent: " " }) => {
  if (count === 0) {
    return value;
  }
  return value.replace(/^(?!\s*$)/gm, indent.repeat(count));
};
export default {
  components: {
    NlyDocsCodeMirror
  },
  data() {
    return {
      html: "",
      js: "",
      compiledJs: null, // Place to hold the transpiled JS code string
      logIdx: 1, // Used as the ":key" on console section for transition hooks
      messages: [],
      vertical: false,
      full: false,
      // Flags for various UI stuff
      isOk: false,
      loading: false,
      ready: false,
      compiling: false,
      building: false
    };
  },
  computed: {
    title() {
      return "在线测试nly-adminlte-vue代码";
    },
    isDefault() {
      // Check if editors contain default JS and template
      return (
        this.js.trim() === DEFAULT_JS.trim() &&
        this.html.trim() === DEFAULT_HTML.trim()
      );
    },
    isBusy() {
      return this.compiling || this.building || this.loading || !this.ready;
    },
    needsTranspiler() {
      return needsTranspiler;
    },
    appData() {
      // Used by our debounced `run` watcher to build the app
      return {
        html: this.html.trim(),
        js: this.compiledJs // Transpiled String or null if transpilation failed
      };
    },
    layout() {
      return this.full ? "全屏" : this.vertical ? "垂直" : "水平";
    },
    exportData() {
      const html = this.html.trim();
      const js = this.js.trim() || "{}";
      let extendedJs =
        js === "{}"
          ? "{ el: '#app' }"
          : js.replace(/^\{/, "{\r\n  el: '#app',");
      extendedJs = `new Vue(${extendedJs})`;
      extendedJs = `window.onload = () => {\r\n${indent(extendedJs, 2)}\r\n}`;
      return {
        html,
        js,
        css: "body { padding: 1rem; }",
        extendedHtml: `<div id="app">\r\n${indent(html, 2)}\r\n</div>`,
        extendedJs,
        externalCss: [
          `//unpkg.com/nly-adminlte-vue@${nlyAdminlteVueVersion}/dist/adminlte/css/adminlte.css`,
          `//unpkg.com/nly-adminlte-vue@${nlyAdminlteVueVersion}/dist/nly-adminlte-vue.css`
        ],
        externalJs: [
          "//unpkg.com/babel-polyfill/dist/polyfill.min.js",
          `//unpkg.com/vue@${vueVersion}/dist/vue.min.js`,
          `//unpkg.com/nly-adminlte-vue@${nlyAdminlteVueVersion}/dist/nly-adminlte-vue.common.js`
        ]
      };
    },
    codepenData() {
      const {
        css,
        extendedHtml,
        extendedJs,
        externalCss,
        externalJs
      } = this.exportData;
      const data = {
        editors: "101",
        layout: "left", // left, right, top
        html_pre_processor: "none",
        css_pre_processor: "none",
        css_prefix: "autoprefixer",
        js_pre_processor: "babel",
        head: '<meta name="viewport" content="width=device-width">',
        css_external: externalCss.join(";"),
        js_external: externalJs.join(";"),
        html: extendedHtml,
        js: extendedJs,
        css
      };
      return JSON.stringify(data);
    },
    codesandboxData() {
      const { html, js, css } = this.exportData;
      const vueContent = [
        "<template>",
        indent(html, 2),
        "</template>",
        "",
        "<style>",
        indent(css, 2),
        "</style>",
        "",
        "<script>",
        indent(`export default ${js}`, 2),
        // prettier-ignore
        '<\/script>' // eslint-disable-line
      ]
        .join("\r\n")
        .replace(/\\\//g, "/");
      const htmlContent = '<div id="app"></div>';
      const jsContent = [
        "import Vue from 'vue'",
        "import { NlyAdminlteVue } from 'nly-adminlte-vue'",
        "import App from './App'",
        "",
        "import 'nly-adminlte-vue/dist/adminlte/css/adminlte.css'",
        "import 'nly-adminlte-vue/dist/adminlte/fontawesome-free/css/all.css'",
        "import 'nly-adminlte-vue/dist/adminlte/icon/iconfont.css'",
        "import 'nly-adminlte-vue/dist/nly-adminlte-vue.css'",
        "",
        "Vue.use(NlyAdminlteVue )",
        "",
        "new Vue({ el: '#app', render: h => h(App) })"
      ].join("\r\n");
      const dependencies = {
        "nly-adminlte-vue": nlyAdminlteVueVersion,
        vue: vueVersion
      };
      return getCodeSandboxParameters({
        files: {
          "App.vue": { content: vueContent },
          "index.html": { content: htmlContent },
          "index.js": { content: jsContent },
          "package.json": { content: { dependencies } }
        }
      });
    },
    fakeConsole() {
      const logger = this.log;
      const clear = this.clear;
      let oConsole, oInfo, oLog, oWarn, oError, oClear;
      try {
        // Native console object & methods
        oConsole = window.console;
        oInfo = window.console.info;
        oLog = window.console.log;
        oWarn = window.console.warn;
        oError = window.console.error;
        oClear = window.console.clear;
      } catch (e) {}
      return {
        info: function() {
          try {
            logger("info", ...arguments);
            oInfo.apply(oConsole, arguments);
          } catch (e) {}
        },
        log: function() {
          try {
            logger("info", ...arguments);
            oLog.apply(oConsole, arguments);
          } catch (e) {}
        },
        warn: function() {
          try {
            logger("warning", ...arguments);
            oWarn.apply(oConsole, arguments);
          } catch (e) {}
        },
        error: function() {
          try {
            logger("danger", ...arguments);
            oError.apply(oConsole, arguments);
          } catch (e) {}
        },
        clear: function() {
          try {
            clear();
            oClear.apply(oConsole);
          } catch (e) {}
        }
      };
    }
  },
  watch: {
    layout(newVal, oldVal) {
      if (newVal !== oldVal) {
        this.saveToStorage();
      }
    }
  },
  created() {
    // Create some non reactive properties
    this.playVM = null;
    this.contentUnWatch = null;
    this.jsUnWatch = null;
    this.run = () => {};
    this.compileJs = () => {};
    // Default code "transpiler"
    this.compiler = code => code;
  },
  beforeMount() {
    // Load content and preferences (or defaults if not available)
    this.loadFromStorage();
  },
  mounted() {
    // Set the loading state if needed
    this.loading = needsTranspiler;
    if (needsTranspiler) {
      this.$nextTick(() => {
        this.$nuxt && this.$nuxt.$loading && this.$nuxt.$loading.start();
        // Lazy load the babel transpiler (in a separate chunk)
        import("../utils/compile-js" /* webpackChunkName: "compile-js" */).then(
          module => {
            // Update compiler reference
            this.compiler = module.default || module;
            // Stop the loading indicator
            this.loading = false;
            this.$nuxt && this.$nuxt.$loading && this.$nuxt.$loading.finish();
            // Run the setup code. We pass 750ms as the debounce
            // timeout, as transpilation can be slow
            this.doSetup(750);
          }
        );
      });
    } else {
      this.doSetup();
    }
  },
  beforeDestroy() {
    // Stop our watchers
    if (this.jsUnWatch) {
      this.jsUnWatch();
    }
    if (this.contentUnWatch) {
      this.contentUnWatch();
    }
    if (!this.$isServer) {
      this.destroyVM();
    }
  },
  methods: {
    doSetup(timeout = 500) {
      // Create our debounced runner
      this.run = debounce(this._run, 500);
      // Create our debounced javascript compiler
      this.compileJs = debounce(this._compileJs, timeout);
      // Set up our editor content watcher
      this.$nextTick(() => {
        // appData watcher
        this.contentUnWatch = this.$watch(
          "appData",
          () => {
            this.run();
          },
          { deep: true }
        );
        // Javascript watcher
        this.jsUnWatch = this.$watch(
          () => this.js.trim(),
          () => {
            this.compileJs();
          },
          { immediate: true }
        );
        // Set ready state
        this.ready = true;
      });
    },
    destroyVM() {
      let vm = this.playVM;
      if (vm) {
        let parent;
        try {
          parent = vm.$parent;
          vm.$destroy();
          removeNode(vm.$el);
          vm.$el.innerHTML = "";
        } catch (err) {}
        try {
          parent.$destroy();
        } catch (err) {}
      }
      this.playVM = vm = null;
      this.$refs.result.innerHTML = "";
    },
    createVM() {
      const playground = this;
      const js = this.compiledJs;
      const html = this.html.trim();
      // Disable the export buttons
      this.isOk = false;
      if (js === null) {
        // Error compiling JS
        return;
      }
      // Options gets assiged to by our eval of the compiled JS
      // eslint-disable-next-line prefer-const
      let options = {};
      // Test JavaScript for syntax errors
      try {
        // Options are eval'ed in our variable scope, so we can override
        // the "global" console reference just for the user app
        /* eslint-disable no-eval */
        eval(`var console = this.fakeConsole; ${js}`);
        /* eslint-enable no-eval */
      } catch (err) {
        this.errHandler(err, "javascript");
        window.console.error("Error in javascript", err);
        return;
      }
      // Sanitize template possibilities
      if (
        !html &&
        typeof options.template !== "string" &&
        typeof options.render !== "function"
      ) {
        this.errHandler("No template or render function provided", "template");
        return;
      } else if (
        !html &&
        typeof options.template === "string" &&
        options.template.charAt(0) === "#"
      ) {
        this.errHandler("Do not set template to an element ID", "template");
        return;
      }
      if (options.el) {
        this.errHandler('Do not set "el" property', "javascript");
        return;
      }
      if (options.render && typeof options.render !== "function") {
        this.errHandler("render must be a function", "javascript");
        return;
      }
      if (!options.render) {
        options.template = `<div id="playground-app">${options.template ||
          html}</div>`;
      } else {
        delete options.template;
      }
      // Vue's `errorCapture` doesn't always handle errors in methods (although it
      // does if the method is used as a `v-on`/`@` handler), so we wrap any methods
      // with a try/catch handler so we can show the error in our GUI log console.
      // Note: Doesn't handle errors in async methods
      // See: https://github.com/vuejs/vue/issues/8568
      if (options.methods) {
        Object.keys(options.methods).forEach(methodName => {
          const fn = options.methods[methodName];
          if (typeof fn !== "function") {
            this.errorHandler(
              `TypeError: ${methodName} is not a function`,
              "methods"
            );
          } else {
            // Replace it with a wrapped method
            options.methods[methodName] = function() {
              try {
                return fn.apply(this, arguments);
              } catch (err) {
                playground.errHandler(err, `method "${methodName}"`);
              }
            };
          }
        });
      }
      // Try and build the user app
      try {
        const holder = document.createElement("div");
        this.$refs.result.appendChild(holder);
        this.playVM = new Vue({
          ...options,
          el: holder,
          // Router needed for tooltips/popovers/toasts so
          // that they hide when docs route changes
          router: this.$router,
          // We set a fake parent so we can capture most runtime and
          // render errors (this is an error boundary component)
          parent: new Vue({
            template: "<span></span>",
            errorCaptured(err, vm, info) {
              // Pass error to playground error handler
              playground.errHandler(err, info);
              // Don't propagate to parent/global error handler!
              return false;
            }
          })
        });
      } catch (err) {
        this.destroyVM();
        this.errHandler(err, "app create");
        return;
      }
      // We got this far, so save the JS/HTML changes to
      // localStorage and enable export buttons
      this.isOk = true;
      this.saveToStorage();
    },
    errHandler(err, info) {
      this.log("danger", `Error in ${info}: ${String(err)}`);
      this.destroyVM();
    },
    _compileJs() {
      if (this.$isServer) {
        this.compiledJs = null;
        return;
      }
      const js = (this.js || "").trim() || "{}";
      this.compiling = true;
      let compiled = null;
      this.$nextTick(() => {
        this.requestAF(() => {
          try {
            // The app build process expects the app options to
            // be assigned to the `options` variable
            compiled = this.compiler(";options = " + js + ";");
          } catch (err) {
            this.errHandler(err, "javascript");
            window.console.error("Error in javascript", err);
            compiled = null;
          }
          this.compiledJs = compiled;
          this.$nextTick(() => {
            this.compiling = false;
          });
        });
      });
    },
    _run() {
      if (this.$isServer) {
        return;
      }
      this.building = true;
      // Destroy old VM if exists
      this.destroyVM();
      // Clear the log
      this.clear();
      this.requestAF(() => {
        // Create and render the instance
        this.createVM();
        this.$nextTick(() => {
          this.building = false;
        });
      });
    },
    toggleVertical() {
      this.vertical = !this.vertical;
    },
    toggleFull() {
      this.full = !this.full;
    },
    log(tag, ...args) {
      // We have to ignore props mutation warning due to a
      // Vue.js bug when we have two instances
      if (String(args[0]).indexOf("Avoid mutating a prop directly") !== -1) {
        return;
      }
      const msg = args.map(String).join(" ");
      if (
        this.messages.length &&
        msg.indexOf("Error in render") !== -1 &&
        msg === this.messages[0][1]
      ) {
        // Prevent duplicate render error messages
        return;
      }
      if (this.messages.length > 10) {
        this.messages.splice(10);
      }
      this.messages.unshift([tag, msg, this.logIdx++]);
    },
    clear() {
      this.logIdx = 1;
      this.messages.splice(0);
    },
    reset() {
      this.$nlyaModal
        .msgBoxConfirm("您确定要重置代码吗？", {
          title: "请确认是否需要重置",
          size: "sm",
          buttonSize: "sm",
          okTitle: "是",
          cancelTitle: "否",
          titleTag: "h6",
          headerClass: "p-2",
          footerClass: "p-2",
          hideHeaderClose: false,
          centered: true
        })
        .then(value => {
          if (value) {
            this.doReset();
          }
        })
        .catch(err => {
          // An error occurred
          console.log(err);
        });
    },
    doReset() {
      // Needed to trick codemirror component to reload contents
      this.js = this.html = "";
      this.$nextTick(() => {
        this.js = DEFAULT_JS.trim();
        this.html = DEFAULT_HTML.trim();
        this.saveToStorage();
      });
    },
    clearStorage() {
      if (!storage) {
        return;
      }
      Object.keys(STORAGE_KEYS).forEach(key => {
        storage.removeItem(key);
      });
    },
    loadFromStorage() {
      if (!storage) {
        this.js = DEFAULT_JS.trim();
        this.html = DEFAULT_HTML.trim();
        return;
      }
      const timestamp =
        parseInt(storage.getItem(STORAGE_KEYS.timestamp), 10) || 0;
      if (Date.now() - timestamp > STORAGE_MAX_RETENTION) {
        this.clearStorage();
      }
      this.html = storage.getItem(STORAGE_KEYS.html) || DEFAULT_HTML.trim();
      this.js = storage.getItem(STORAGE_KEYS.js) || DEFAULT_JS.trim();
      const layout = storage.getItem(STORAGE_KEYS.layout) || "horizontal";
      if (layout === "全屏") {
        this.full = true;
      } else if (layout === "垂直") {
        this.vertical = true;
        this.full = false;
      } else if (layout === "水平") {
        this.vertical = false;
        this.full = false;
      }
    },
    saveToStorage() {
      if (!storage) {
        return;
      }
      try {
        storage.setItem(STORAGE_KEYS.html, this.html);
        storage.setItem(STORAGE_KEYS.js, this.js);
        storage.setItem(STORAGE_KEYS.layout, this.layout);
        storage.setItem(STORAGE_KEYS.timestamp, String(Date.now()));
      } catch (err) {
        // Silently ignore errors on safari iOS private mode
      }
    },
    requestAF(fn) {
      const w = typeof window === "undefined" ? {} : window;
      const raf =
        w.requestAnimationFrame ||
        w.webkitRequestAnimationFrame ||
        w.mozRequestAnimationFrame ||
        w.msRequestAnimationFrame ||
        w.oRequestAnimationFrame ||
        // Fallback, but not a true polyfill
        // Only needed for Opera Mini
        (cb => setTimeout(cb, 16));
      return raf(fn);
    }
  },
  head() {
    const title = `${this.title} | NlyAdminlteVue`;
    const description = "测试NlyAdminlteVu的组件和指令";
    return {
      title,
      meta: [
        {
          hid: "og:title",
          name: "og:title",
          property: "og:title",
          content: title
        },
        {
          hid: "og:description",
          name: "og:description",
          property: "og:description",
          content: description
        },
        {
          hid: "description",
          name: "description",
          content: description
        }
      ]
    };
  }
};
</script>
